package cn.autumnorange.app.common.provider.cacheannotation.strategy;

import cn.autumnorange.app.common.consumerproducer.util.ClassUtil;
import cn.autumnorange.app.common.dto.ResultDto;
import cn.autumnorange.app.common.provider.cacheannotation.RedisCacheTarget;
import cn.autumnorange.app.common.provider.cacheannotation.filter.SetRedissionFilter;
import cn.autumnorange.app.common.provider.cacheannotation.filter.StringRedissionFilter;
import cn.autumnorange.app.common.rpc.anntation.AbstractAnnotationStrategy;
import cn.autumnorange.app.common.rpc.anntation.AnnotationStrategy;
import cn.autumnorange.app.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.http.ResponseEntity;

import java.beans.ExceptionListener;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;

/** 读缓存加锁策略处理 */
@Slf4j
public class RedisCacheReadAnnotationnStrategy extends AbstractAnnotationStrategy {
  int i = 0;
  private long connectTimeout;
  private long readTimeout;
  private RedissonClient redissonClient;
  private RedissonContext redissonContext;
  public static final String REDISCACHELOCKPREFIX = "providerRedisCacheLock#";
  public static final String ANNOTATIONSTRATEGY = RedisCacheTarget.class.getName();

  public RedisCacheReadAnnotationnStrategy(
      RedissonClient redissonClient, long connectTimeout, long readTimeout) {
    this.redissonClient = redissonClient;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    RedissonContext redissonContext = RedissonContext.getRedissonContext(redissonClient);

    redissonContext.setNextFilter(new StringRedissionFilter(redissonClient));
    redissonContext.setNextFilter(new SetRedissionFilter(redissonClient));
    this.redissonContext = redissonContext;
  }
  // 通过注解字段对应入参对象的相同类型,相同字段名称的字段来查询缓存
  @Override
  public Object annotationTargetHandle(MethodInvocation invocation, Annotation annotation)
      throws Throwable {
    Throwable throwable = null;
    RedisCacheTarget redisCacheTargt = (RedisCacheTarget) annotation;
    // 获取注解参数
    Parameter[] parameters = invocation.getMethod().getParameters();

    String cacheType = redisCacheTargt.cacheType();
    if (StringUtils.isBlank(cacheType)) {
      throw new RuntimeException("redisCacheTargt cacheType is not null");
    }

    // 获取方法参数名
    //    ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
    // 读缓存加读锁 ,获取不到读数据库加写锁将数据库数据写缓存
    // 写数据加写锁 写数据库,写缓存
    RReadWriteLock rReadWriteLock =
        redissonClient.getReadWriteLock(REDISCACHELOCKPREFIX + redisCacheTargt.fieldKeys());

    RLock readRLock = rReadWriteLock.readLock();
    Object result = null;
    RLock writeRLock = null;
    // 读缓存加读锁 读锁未正常锁住不执行其他业务操作 异常继续读库或者直接异常处理？
    try {
      //    // 对指定参数加读锁,有写锁时会被卡住
      //      readRLock.lockAndStartWatchDogAsync(9000, TimeUnit.MILLISECONDS);
      readRLock.tryLockAndStartWatchDog(0, 9000, TimeUnit.MILLISECONDS);
      //      readRLock.lock();

      /** 获取缓存数据 有对应的key但是数据类型不对时报错 加锁key存储在redis中也是唯一的 加锁跟存储key同名会导致类型错误 */
      log.info(
          "redis 缓存加读锁结果:"
              + (readRLock != null && readRLock.isLocked() && readRLock.isHeldByCurrentThread()));
      // Async异步加锁方法这里调用 isLocked()可能由于异步加锁还没执行会导致返回false
      if (readRLock != null && readRLock.isLocked() && readRLock.isHeldByCurrentThread()) {
        result = redissonContext.doFilter(cacheType, redisCacheTargt.fieldKeys());
        // 读取缓存后释放锁
        readRLock.unlock();
        if (result == null) {
          writeRLock = rReadWriteLock.writeLock();
          if (readRLock == writeRLock) {
            log.info("readRLock=writeRLock");
          } else {
            log.info("readRLock!=writeRLock");
          }
          //          writeRLock.lockAndStartWatchDogAsync(connectTimeout + readTimeout,
          // TimeUnit.MILLISECONDS);
          writeRLock.tryLockAndStartWatchDog(
              0, connectTimeout + readTimeout, TimeUnit.MILLISECONDS);
          //          writeRLock.lock();
          // Async异步加锁方法这里调用 isLocked()可能由于异步加锁还没执行会导致返回false

          if (writeRLock != null && writeRLock.isLocked() && writeRLock.isHeldByCurrentThread()) {
            result = invocation.proceed();
          } else {
            log.error(
                "redis 缓存加写锁异常:readRLock==null ? "
                    + (writeRLock != null)
                    + " isLocked ?"
                    + writeRLock.isLocked()
                    + " isHeldByCurrentThread() ?"
                    + writeRLock.isHeldByCurrentThread());
          }
        } else {
          ResultDto resultDto = ResultDto.createDataResult(result);
          result = ResponseEntity.ok(resultDto);
        }
      } else {
        log.error(
            "redis 缓存加读锁异常:readRLock==null ? "
                + (readRLock != null)
                + " isLocked ?"
                + readRLock.isLocked()
                + " isHeldByCurrentThread() ?"
                + readRLock.isHeldByCurrentThread());
      }
    } catch (Throwable e) {
      log.error("redis 缓存查询异常:" + e.getMessage());
      throwable = e;
    } finally {
      //      异常时需要先释放锁再抛异常
      if (readRLock.isLocked() && readRLock.isHeldByCurrentThread()) {
        readRLock.unlock();
      }
      if (writeRLock != null && writeRLock.isLocked() && writeRLock.isHeldByCurrentThread()) {
        // 写入缓存 最后再释放锁
        if (result != null) {
          ResponseEntity<ResultDto<String>> responseEntity =
              (ResponseEntity<ResultDto<String>>) result;
          ResultDto resultDto = (ResultDto) responseEntity.getBody();
          Object object = resultDto.getData();
          Object targetObj = null;
          Object[] arguments = new Object[] {object};
          if (arguments != null && arguments.length > 0) {
            LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer =
                new LocalVariableTableParameterNameDiscoverer();
            String[] parameterNames =
                localVariableTableParameterNameDiscoverer.getParameterNames(invocation.getMethod());
            //            targetObj =
            //                ClassUtil.getTargetFeildObjByClassFieldName(
            //                        fieldNameTypeArray, arguments, parameterNames);
            String[] fieldKeys = redisCacheTargt.fieldKeys().split("#");
            targetObj = ClassUtil.getTargetFeildByFeildKey(fieldKeys, arguments, parameterNames);
          }
          if (targetObj == null) {
              log.info("生产端线程:"+Thread.currentThread().getId()+ " "+Thread.currentThread().getName()+ " 获取服务数据对应的缓存字段null 请检查注解指定的字段是否有误:" );

          } else {
            // 在注解中找到对应属性字段存入缓存
            RBucket<Object> nameRBucket = redissonClient.getBucket(redisCacheTargt.fieldKeys());
            if (redisCacheTargt.cacheTime() > 0) {

              nameRBucket.set(targetObj, redisCacheTargt.cacheTime(), TimeUnit.MILLISECONDS);
            } else {
              nameRBucket.set(targetObj);
            }
          }
        }
        writeRLock.unlock();
      }
      if (throwable != null) {
        throw throwable;
      }
      ResultDto resultDto = null;
      if (result != null) {
        return result;
      } else {
        resultDto =
            ResultDto.createResult(
                404, resultDto, "not found cache " + redisCacheTargt.fieldKeys(), null, null, null);
      }

      return ResponseEntity.ok(resultDto);
    }

    //        if(Class.forName(dataType) instanceof String){
    //
    //        }

    //        for(int i=0;i<arguments.length;i++){
    //            System.out.println("argument "+argument);
    //            System.out.println("argument value "+argument.toString());
    //        }

  }
}
